use crate::{
    assistant::{model::Model, prompts::PromptTemplateItem},
    datasource::{QueryCondition, QueryExtention},
    error::Error,
    modeling::{Value, ValueType},
};
use derive_builder::Builder;
use serde::{Deserialize, Serialize};
use std::borrow::BorrowMut;
use std::{
    collections::HashMap,
    sync::{Arc, Mutex},
};

use super::{
    DataOperatorMethod, PromptSpeechFile, PromptSpeechSelector, PromptSpeechTemplate,
    PromptSpeechType, ValueSelector, VariableAggregatorAdvancedSettings, WorkflowCase,
    WorkflowDocument, WorkflowErrorStrategy, WorkflowHost, WorkflowHttpAuthorization,
    WorkflowHttpBody, WorkflowHttpMethod, WorkflowHttpRequestTimeout, WorkflowInputVariable,
    WorkflowListExtract, WorkflowListFilter, WorkflowListLimit, WorkflowListOrder,
    WorkflowLlmContext, WorkflowModel, WorkflowScriptLanguage, WorkflowTTSOptions,
    WorkflowVariable,
};

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder)]
#[builder(name = "WorkflowBlockChainBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct WorkflowBlockChain {
    pub source: Option<WorkflowDocument>,
    pub root: WorkflowBlockRef,
    pub index: HashMap<String, WorkflowBlockRef>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder)]
#[builder(name = "WorkflowBlockBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct WorkflowBlock {
    pub id: String,
    pub inputs: HashMap<String, WorkflowBlockRef>,
    pub outputs: HashMap<String, WorkflowBlockRef>,
    pub body: WorkflowBlockBody,
}

unsafe impl Send for WorkflowBlock {}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "type")]
pub enum WorkflowBlockBody {
    #[serde(rename = "start")]
    Start(WorkflowBlockStart),
    #[serde(rename = "if-else")]
    IfElse(WorkflowBlockIfElse),
    #[serde(rename = "case")]
    Case(WorkflowBlockCase),
    #[serde(rename = "foreach")]
    ForEach(WorkflowBlockForEach),
    #[serde(rename = "trycatch")]
    TryCatch(WorkflowBlockTryCatch),
    #[serde(rename = "statement")]
    Statement(WorkflowBlockStatement),
    #[serde(rename = "return")]
    Return(WorkflowBlockReturn),
}
impl Default for WorkflowBlockBody {
    fn default() -> Self {
        Self::Statement(WorkflowBlockStatement::Empty)
    }
}

pub type WorkflowBlockRef = Arc<Mutex<WorkflowBlock>>;

#[derive(Debug, Serialize, Deserialize, Default, Clone, Builder)]
#[builder(name = "WorkflowBlockIfElseBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct WorkflowBlockIfElse {
    pub cases: Vec<WorkflowCase>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct WorkflowBlockCase {
    cases: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct WorkflowBlockIteration {}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder)]
#[builder(name = "WorkflowBlockForEachBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct WorkflowBlockForEach {
    pub root: WorkflowBlockRef,
    pub iterator: Option<ValueSelector>,
    pub output: ValueSelector,
    pub is_parallel: bool,
    pub parallel_nums: u32,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct WorkflowBlockTryCatch {}

#[derive(Debug, Default, Serialize, Deserialize, Clone)]
#[serde(tag = "statement")]
pub enum WorkflowBlockStatement {
    #[serde(rename = "empty")]
    #[default]
    Empty,
    #[serde(rename = "http")]
    Http(HttpStatement),
    #[serde(rename = "answer")]
    Answer(AnswerStatement),
    #[serde(rename = "llm")]
    LLM(LlmStatement),
    #[serde(rename = "speech-generator")]
    SpeechGenerator(SpeechGeneratorStatement),
    #[serde(rename = "image-generator")]
    ImageGenerator(ImageGeneratorStatement),
    #[serde(rename = "video-generator")]
    VideoGenerator(VideoGeneratorStatement),
    #[serde(rename = "classifier")]
    Classifier(ClassifierStatement),
    #[serde(rename = "list-operator")]
    ListOperator(ListOperatorStatement),
    #[serde(rename = "variable-aggregator")]
    VariableAggregator(VariableAggregatorStatement),
    #[serde(rename = "meta-operator")]
    MetaOperator(MetaOperatorStatement),
    #[serde(rename = "data-operator")]
    DataOperator(DataOperatorStatement),
    #[serde(rename = "data-extractor")]
    DataExtractor(DataExtractorStatement),
    #[serde(rename = "html-extractor")]
    HtmlExtractor(HtmlExtractorStatement),
    #[serde(rename = "document-extractor")]
    DocumentExtractor(DocumentExtractorStatement),
    #[serde(rename = "sql-operator")]
    SQL(SqlStatement),
    #[serde(rename = "knowledge")]
    Knowledge(KnowledgeStatement),
    #[serde(rename = "prompts")]
    Prompts(PromptsStatement),
    #[serde(rename = "pubsub_publish")]
    PubsubPublish(PubsubPublishStatement),
    #[serde(rename = "pubsub_subscribe")]
    PubsubSubscribe(PubsubSubscribeStatement),
    #[serde(rename = "script")]
    Script(ScriptStatement),
    #[serde(rename = "wait")]
    Wait(WaitStatement),
    #[serde(rename = "cli")]
    CLI(CliStatement),
    #[serde(rename = "template")]
    Template(TemplateStatement),
    #[serde(rename = "wasm")]
    Wasm(WasmStatement),
    #[serde(rename = "workflow")]
    Workflow(WorkflowStatement),
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, Builder)]
#[builder(name = "WaitStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct WaitStatement {
    pub cases: Option<WorkflowCase>,
    pub timeout: Option<u64>,
    pub interval: Option<u64>,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, Builder)]
#[builder(name = "HttpStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct HttpStatement {
    pub url: String,
    pub method: WorkflowHttpMethod,
    pub authorization: WorkflowHttpAuthorization,
    pub headers: HashMap<String, String>,
    pub params: HashMap<String, String>,
    pub body: WorkflowHttpBody,
    pub timeout: Option<WorkflowHttpRequestTimeout>,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, Builder)]
#[builder(name = "ListOperatorStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct ListOperatorStatement {
    pub variable: ValueSelector,
    pub filter_by: WorkflowListFilter,
    pub extract_by: WorkflowListExtract,
    pub order_by: WorkflowListOrder,
    pub limit: WorkflowListLimit,
    pub var_type: String,
    pub item_var_type: String,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, Builder, PartialEq)]
#[builder(name = "VariableAggregatorStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct VariableAggregatorStatement {
    pub variables: Vec<ValueSelector>,
    pub advanced_settings: VariableAggregatorAdvancedSettings,
    pub output_type: String,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, Builder)]
#[builder(name = "MetaOperatorStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct MetaOperatorStatement {
    pub class_type: String,
    pub identifier: String,
    pub method: String,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, Builder)]
#[builder(name = "DataOperatorStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct DataOperatorStatement {
    pub domain: String,
    pub class: String,
    pub method: DataOperatorMethod,
    pub datasource: Option<String>,
    pub conditions: Option<QueryCondition>,
    pub extentions: Option<QueryExtention>,
    pub value: Option<HashMap<String, serde_json::Value>>,
    pub options: Option<HashMap<String, serde_json::Value>>,
    pub outputs: HashMap<String, ValueType>,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct AnswerStatement {}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct ClassifierStatement {}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder, PartialEq)]
#[builder(name = "LlmStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct LlmStatement {
    pub model: WorkflowModel,
    pub prompt_template: Vec<PromptTemplateItem>,
    pub context: WorkflowLlmContext,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder, PartialEq)]
#[builder(name = "SpeechGeneratorStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct SpeechGeneratorStatement {
    pub model: WorkflowModel,
    pub prompt_speech_type: PromptSpeechType,
    pub prompt_speech_selector: Option<PromptSpeechSelector>,
    pub prompt_speech_template: Option<PromptSpeechTemplate>,
    pub prompt_speech: Option<PromptSpeechFile>,
    pub text: String,
    pub options: WorkflowTTSOptions,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder, PartialEq)]
#[builder(name = "VideoGeneratorStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct VideoGeneratorStatement {
    pub model: WorkflowModel,
    pub prompt_text: String,
    pub prompt_speech: String,
    pub text: String,
    pub options: WorkflowTTSOptions,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder, PartialEq)]
#[builder(name = "ImageGeneratorStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct ImageGeneratorStatement {
    pub model: WorkflowModel,
    pub prompt_text: String,
    pub prompt_speech: String,
    pub text: String,
    pub options: WorkflowTTSOptions,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct DataExtractorStatement {}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct KnowledgeStatement {}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct PubsubPublishStatement {}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct PubsubSubscribeStatement {}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct PromptsStatement {}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder, PartialEq)]
#[builder(name = "ScriptStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct ScriptStatement {
    pub language: WorkflowScriptLanguage,
    pub script: String,
    pub variables: Vec<WorkflowInputVariable>,
    pub outputs: HashMap<String, ValueType>,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder, PartialEq)]
#[builder(name = "CliStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct CliStatement {
    pub variables: Vec<WorkflowInputVariable>,
    pub host: Option<WorkflowHost>,
    pub current_dir: Option<String>,
    pub command: String,
    pub arguments: Vec<String>,
    pub timeout: Option<u32>,
    pub stdout: Option<bool>,
    pub stderr: Option<bool>,
    pub outputs: HashMap<String, ValueType>,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder, PartialEq)]
#[builder(name = "SqlStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct SqlStatement {
    pub variables: Vec<WorkflowInputVariable>,
    pub script: String,
    pub is_query: bool,
    pub datasource: Option<ValueSelector>,
    pub outputs: HashMap<String, ValueType>,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder, PartialEq)]
#[builder(name = "HtmlExtractorStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct HtmlExtractorStatement {
    pub url: String,
    pub selector: String,
    pub attribute: Option<String>,
    pub text: Option<bool>,
    pub outputs: HashMap<String, ValueType>,
    pub timeout: Option<WorkflowHttpRequestTimeout>,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder, PartialEq)]
#[builder(name = "DocumentExtractorStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct DocumentExtractorStatement {
    pub variable_selector: ValueSelector,
    pub is_array_file: Option<bool>,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct TemplateStatement {}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder)]
#[builder(name = "WasmStatementBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct WasmStatement {
    pub file: String,
    pub function_name: String,
    pub arguments: Option<String>,
    pub timeout: Option<WorkflowHttpRequestTimeout>,
    pub outputs: HashMap<String, ValueType>,
    pub error_strategy: Option<WorkflowErrorStrategy>,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct WorkflowStatement {}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct WorkflowBlockVariable {}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder)]
#[builder(name = "WorkflowBlockReturnBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct WorkflowBlockReturn {
    pub exports: Vec<WorkflowBlockReturnVariable>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder, PartialEq)]
#[builder(name = "WorkflowBlockStartBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct WorkflowBlockStart {
    pub variables: Vec<WorkflowVariable>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, Builder)]
#[builder(name = "WorkflowBlockReturnVariableBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct WorkflowBlockReturnVariable {
    pub variable: String,
    pub value_selector: ValueSelector,
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq)]
pub enum WorkflowBlockExecuteResultStatus {
    #[default]
    #[serde(rename = "succeeded")]
    Succeeded,
    #[serde(rename = "failed")]
    Failed,
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, Builder, PartialEq)]
#[builder(name = "WorkflowBlockExecuteResultBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct WorkflowBlockExecuteResult {
    pub status: WorkflowBlockExecuteResultStatus,
    pub is_return: bool,
    pub source_handle: Option<String>,
    pub result: HashMap<String, Value>,
    pub exceptions: Vec<Value>,
}

impl WorkflowBlockExecuteResult {
    pub fn succeed() -> Self {
        Self {
            status: WorkflowBlockExecuteResultStatus::Succeeded,
            result: HashMap::new(),
            source_handle: None,
            is_return: false,
            exceptions: vec![],
        }
    }
}

impl WorkflowBlock {
    pub fn body(body: WorkflowBlockBody) -> Self {
        Self {
            id: String::new(),
            inputs: HashMap::new(),
            outputs: HashMap::new(),
            body: body,
        }
    }
}

pub trait SetIterationRoot {
    fn set_root(&self, root: WorkflowBlockRef) -> anyhow::Result<()>;
}

impl SetIterationRoot for WorkflowBlockRef {
    /// 设置
    fn set_root(&self, root: WorkflowBlockRef) -> anyhow::Result<()> {
        let mut block = self.lock().map_err(|err| anyhow::anyhow!("{}", err))?;
        let body = &mut block.borrow_mut().body;
        match body {
            WorkflowBlockBody::ForEach(v) => {
                v.root = root;
            }
            _ => {}
        }
        Ok(())
    }
}

impl HttpStatement {
    pub fn params_from_string(&mut self, params: &String) -> anyhow::Result<()> {
        //text:您好\nvoice_name:prompt_audio
        let items: Vec<&str> = params.split('\n').collect();
        for item in items {
            if let Some((key, value)) = item.split_once(':') {
                self.params.insert(String::from(key), String::from(value));
            }
        }
        Ok(())
    }
    pub fn headers_from_string(&mut self, headers: &String) -> anyhow::Result<()> {
        //text:您好\nvoice_name:prompt_audio
        let items: Vec<&str> = headers.split('\n').collect();
        for item in items {
            if let Some((key, value)) = item.split_once(':') {
                self.headers.insert(String::from(key), String::from(value));
            }
        }
        Ok(())
    }
}
